package com.jhhc.IoUtils;

import java.lang.reflect.*;
import java.sql.*;
import java.util.*;


public class DBHelper {



    // 定义共用的连接对象

    private Connection conn;



    private boolean isAutoCommit = true;
    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    static final String DB_URL = "jdbc:mysql://localhost:3306/aaa";


    // 数据库的用户名与密码，需要根据自己的设置
    static final String USER = "root";
    static final String PASS = "123456";
//static final String JDBC_DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
//    static final String DB_URL = "jdbc:sqlserver://123.60.35.102:1433;DatabaseName=RHDB_JHHC";
//
//
//    // 数据库的用户名与密码，需要根据自己的设置
//    static final String USER = "JhhcUser";
//    static final String PASS = "RiverStrong.522";

    /**
     * 	类的代码块:
     * 	静态块:
     * 		static {}
     * 		特点: 会在类被加载到虚拟机时,执行一次(  例如: import 类)
     * 	实例块
     * 		{}
     * 		特点: 会在对象被创建时执行一次, 在构造方法前
     * 	块不是方法,不能抛出编译期异常
     */
    static {
        try {
            Class.forName(JDBC_DRIVER);
        } catch (ClassNotFoundException e) {
            // 异常转型 ==> 异常链
            // 未捕获的运行期异常将导致程序的终止
            RuntimeException re = new RuntimeException("数据库驱动加载失败!", e);
            throw re;
        }
    }

    /**
     * 使用 isAutoCommit 决定是否自动提交
     *
     * 如果是自动提交, 则意味着每次执行 update 方法都要获取新的连接, 在执行之后关闭连接
     * 否则, 不关闭连接
     * @param isAutoCommit  自动提交  true
     */
    public DBHelper(boolean isAutoCommit) {
        this.isAutoCommit = isAutoCommit;
        if(isAutoCommit == false) {
            conn = openConnection();
        }
    }

    /**
     * JDBC 连接默认是自动提交, 也就是每次执行完增删改都会自动提交
     * 无参的构造方法, 可以注释掉了
     */
    public DBHelper() {
        // 在构造方法中创建连接
        //conn = openConnection();
    }

    // 关闭连接
    public void closeConnection() {
        IOHelper.close(conn);
    }

    // 返回连接对象
    public Connection getConn() {
        return conn;
    }

    /**
     * 	获取连接
     * @return
     */
    public Connection openConnection() {

        try {
            if(isAutoCommit) {
                return DriverManager.getConnection(DB_URL,USER,PASS);
            } else {
                if(conn == null) {
                    // 禁止自动提交
                    conn = DriverManager.getConnection(DB_URL,USER,PASS);
                    conn.setAutoCommit(isAutoCommit);
                }
                return conn;
            }
        } catch (SQLException e) {
            throw new RuntimeException("获取数据库连接失败!", e);
        }
    }

    /**
     * 	执行修改数据库的语句
     * sql = "update emp set ename = ? where empno=?"
     * update(sql,2,3,)
     * @param sql		执行的sql语句
     * @param params	可变参数数组
     * @return
     */
    public int update(String sql, Object... params) {
        try {
            // 每次都会通过open方法获取连接
            conn = openConnection();
            System.out.println("SQL: " + sql);
            PreparedStatement ps = conn.prepareStatement(sql);
            // alrt + /

            System.out.println("参数: " + Arrays.toString(params));
            for (int i = 0; i < params.length; i++) {
                boolean res = params[i].equals("null");
                if (!res){
                    ps.setObject(i+1, params[i]);
                }
                else
                {
                    ps.setObject(i+1, null);
                }
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("执行SQL语句失败!", e);
        } finally {
            if(isAutoCommit == true) {
                IOHelper.close(conn);
            }
        }
    }

    /**
     * 	执行查询语句
     * @param sql
     * @param params
     * @return
     */
    public List<Map<String, Object>> query(String sql, Object... params) {
        try {
            conn = openConnection();
            System.out.println("SQL: " + sql);
            PreparedStatement ps = conn.prepareStatement(sql);
            // alrt + /
            System.out.println("参数: " + Arrays.toString(params));
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            ResultSet rs = ps.executeQuery();

            // 获取结果集元数据对象, 元(Meta)数据(data): 描述数据的数据
            ResultSetMetaData rsmd = rs.getMetaData();
            // 创建返回结果对象
            List<Map<String, Object>> ret = new ArrayList<>();
            while (rs.next()) {
                // 创建 map 集合
                /**
                 * 1. HashMap   	无序不重复
                 * 2 LinkedHashMap, 有序不重复
                 * 3. TreeMap 		排序不重复
                 */
                Map<String, Object> row = new LinkedHashMap<>();
                // 获取每一个字段值, 设置到一个map中
                for (int i = 0; i < rsmd.getColumnCount(); i++) {
                    String columnName = rsmd.getColumnName(i + 1);
                    Object columnValue = rs.getObject(columnName);
                    row.put(columnName, columnValue);
                }
                // 将 map 添加到 ret 中
                ret.add(row);
            }
            return ret;
        } catch (SQLException e) {
            throw new RuntimeException("执行SQL语句失败!", e);
        } finally {
            if(isAutoCommit == true) {
                IOHelper.close(conn);
            }
        }
    }

    /**
     * 	返回值的类型是可变的类型, 所有的集合==> 泛型类
     * 	query 方法改造成 泛型方法	: 语法的定义: 在方法前用 <E>
     *
     * @param sql
     * @param cls		类对象, 表示 E 类的类对象, Java 反射技术
     * @param params
     * @return
     */
    public <E> List<E> query(String sql, Class<E> cls, Object... params) {
        try {
            conn = openConnection();
            System.out.println("SQL: " + sql);
            PreparedStatement ps = conn.prepareStatement(sql);
            // alrt + /
            System.out.println("参数: " + Arrays.toString(params));
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            ResultSet rs = ps.executeQuery();

            // 获取结果集元数据对象, 元(Meta)数据(data): 描述数据的数据
            ResultSetMetaData rsmd = rs.getMetaData();
            // 创建返回结果对象
            List<E> ret = new ArrayList<>();
            while (rs.next()) {
                // 创建 实体对象集合( 通过反射机制创建实体对象  == new 实体类()   )
                E e;
                try {
                    e = cls.newInstance();
                } catch (Exception e2) {
                    // 异常转型
                    throw new RuntimeException(e2);
                }

                // 通过反射进行属性值的设置
                for (int i = 0; i < rsmd.getColumnCount(); i++) {
                    try {
                        // 根据当前的列名找对应的属性
                        String columnName = rsmd.getColumnName(i+1); // ID, NAME, AUTHER ...
                        columnName = columnName.toLowerCase(); // 转小写
                        // 获取该类定义的属性(包括私有)
                        Field field = cls.getDeclaredField(columnName);
                        // 获取当前列的值
                        /**
                         *  ID ==> JDBC 数据类型 : BigDecimal 大实数 表示任意大小的数字
                         *  	  	    实体类类型: Long
                         *  .getType 获取属性的类型  ==> LONG  String  Integer
                         */
                        // 从结果取出的数值
                        //Object value = rs.getObject(i+1);
                        // 要转换的数值
                        Object destValue = null;
                        // 一定要判断非空, 否则会导致类型转换错误
//						if(value==null) {
//							continue;
//						}
                        if(field.getType().equals(Long.class)) {
                            destValue = rs.getLong(i+1);
                            //destValue = Long.valueOf(value + "");
                        } else if(field.getType().equals(Integer.class)) {
                            destValue = rs.getInt(i+1);
                            //destValue = Integer.valueOf(value + "");
                        } else if(field.getType().equals(Double.class)) {
                            destValue = rs.getDouble(i+1);
                            //destValue = Double.valueOf(value + "");
                        } else if(field.getType().equals(Byte.class)) {
                            destValue = rs.getByte(i+1);
                            //destValue = Byte.valueOf(value + "");
                        } else if(field.getType().equals(Boolean.class)) {
                            destValue = rs.getBoolean(i+1);
                            //destValue = Boolean.valueOf(value + "");
                        } else if(field.getType().equals(Timestamp.class)) {
                            destValue = rs.getTimestamp(i+1);
                            //destValue = Byte.valueOf(value + "");

                            // 其他数据类型请自行添加
                        } else {
                            destValue = rs.getObject(i+1);
                        }
                        // 设置强制访问私有属性
                        field.setAccessible(true);
                        // 将值设置到该属性中
                        field.set(e, destValue);
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                }
                // 将 map 添加到 ret 中
                ret.add(e);
            }
            return ret;
        } catch (SQLException e) {
            throw new RuntimeException("执行SQL语句失败!", e);
        } finally {
            if(isAutoCommit == true) {
                IOHelper.close(conn);
            }
        }
    }

    public List<Map<String, Object>> query1( String sql,Class<Map<String, Object>> cls, Object... params) {
        try {
            conn = openConnection();
            System.out.println("SQL: " + sql);
            PreparedStatement ps = conn.prepareStatement(sql);
            // alrt + /
            System.out.println("参数: " + Arrays.toString(params));
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            ResultSet rs = ps.executeQuery();

            // 获取结果集元数据对象, 元(Meta)数据(data): 描述数据的数据
            ResultSetMetaData rsmd = rs.getMetaData();
            // 创建返回结果对象
            List<Map<String, Object>> ret = new ArrayList<>();
            while (rs.next()) {
                // 创建 实体对象集合( 通过反射机制创建实体对象  == new 实体类()   )
                Map<String, Object> e;
                try {
                    e = cls.newInstance();
                } catch (Exception e2) {
                    // 异常转型
                    throw new RuntimeException(e2);
                }

                // 通过反射进行属性值的设置
                for (int i = 0; i < rsmd.getColumnCount(); i++) {
                    try {
                        // 根据当前的列名找对应的属性
                        String columnName = rsmd.getColumnName(i+1);
                        // ID, NAME, AUTHER ...
                        columnName = columnName.toLowerCase();
                        // 转小写
                        // 获取该类定义的属性(包括私有)
                        Field field = cls.getDeclaredField(columnName);
                        // 获取当前列的值
                        /**
                         *  ID ==> JDBC 数据类型 : BigDecimal 大实数 表示任意大小的数字
                         *  	  	    实体类类型: Long
                         *  .getType 获取属性的类型  ==> LONG  String  Integer
                         */
                        // 从结果取出的数值
                        //Object value = rs.getObject(i+1);
                        // 要转换的数值
                        Object destValue = null;
                        // 一定要判断非空, 否则会导致类型转换错误
//						if(value==null) {
//							continue;
//						}
                        if(field.getType().equals(Long.class)) {
                            destValue = rs.getLong(i+1);

                        } else if(field.getType().equals(Integer.class)) {
                            destValue = rs.getInt(i+1);

                        } else if(field.getType().equals(Double.class)) {
                            destValue = rs.getDouble(i+1);

                        } else if(field.getType().equals(Byte.class)) {
                            destValue = rs.getByte(i+1);

                        } else if(field.getType().equals(Boolean.class)) {
                            destValue = rs.getBoolean(i+1);

                        } else if(field.getType().equals(Timestamp.class)) {
                            destValue = rs.getTimestamp(i+1);
                            // 其他数据类型请自行添加
                        } else {
                            destValue = rs.getObject(i+1);
                        }
                        // 设置强制访问私有属性
                        field.setAccessible(true);
                        // 将值设置到该属性中
                        field.set(e, destValue);
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                }
                // 将 map 添加到 ret 中
                ret.add(e);
            }
            return ret;
        } catch (SQLException e) {
            throw new RuntimeException("执行SQL语句失败!", e);
        } finally {
            if(isAutoCommit == true) {
                IOHelper.close(conn);
            }
        }
    }
}


